package com.scs.application.modules.sys.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.consts.Const;
import com.scs.application.core.entity.DataEntity;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.mapper.IBaseMapper;
import com.scs.application.core.mvc.controller.CrudController;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.service.IBaseService;
import com.scs.application.core.utils.FileUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.modules.sys.dto.CreateCRUDFileDTO;
import com.scs.application.modules.sys.dto.CreateFrontFileDTO;
import com.scs.application.modules.sys.dto.TableColumnDTO;
import com.scs.application.modules.sys.mapper.DataViewMapper;
import com.scs.application.modules.sys.service.ProgramService;
import com.scs.application.modules.sys.utils.UtilDatabase;
import com.scs.application.modules.sys.vo.TableColumn;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

@Service
@Configuration
@Slf4j
public class ProgramServiceImpl implements ProgramService {

    @Value("${spring.datasource.dynamic.datasource.master.username}")
    private String userName;
    @Value("${spring.datasource.dynamic.datasource.master.url}")
    private String url;
    @Value("${spring.datasource.dynamic.datasource.master.driver-class-name}")
    private String driverClassName;
    @Value("${spring.datasource.dynamic.datasource.master.password}")
    private String password;
    @Autowired
    private JdbcTemplate jdbcTemplateDefault;

    @Autowired
    private DataViewMapper dataViewMapper;

    @Override
    public Boolean createCRUD(CreateCRUDFileDTO para) {
        AutoGenerator mpg = new AutoGenerator();
        List<TableColumn> fields = dataViewMapper.getTableColumns(para.getTableName());
        for (TableColumn field : fields) {
            field.setExtendInfo();
        }
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        if (!FileUtil.exist(Const.AUTO_CODE_PATH)) {
            Const.AUTO_CODE_PATH =  "F:\\_build_file\\";
            if (!FileUtil.exist(Const.AUTO_CODE_PATH)) {
                Const.AUTO_CODE_PATH =  "C:\\_build_file\\";
            }
        }
        gc.setOutputDir(Const.AUTO_CODE_PATH);
        gc.setFileOverride(true);
        gc.setActiveRecord(true);
        gc.setEnableCache(true);// XML 二级缓存
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(true);// XML columList
        gc.setAuthor("Auto Code");

        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setControllerName("%sController");
        gc.setEnableCache(false);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);

        dsc.setDriverName(this.driverClassName);
        dsc.setUrl(this.url);
        dsc.setUsername(this.userName);
        dsc.setPassword(this.password);
        mpg.setDataSource(dsc);


        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 使用自定义模板，不想要生成就设置为null,如果不设置null会使用默认模板
        templateConfig.setController("templates/Controller.java");
        templateConfig.setEntity("templates/entity.java");

        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setEntityLombokModel(true);
        // strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意
        strategy.setTablePrefix(para.getTablePrefix());// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        strategy.setInclude(new String[]{para.getTableName()}); // 需要生成的表
        // strategy.setExclude(new String[]{"test"}); // 排除生成的表
        strategy.setSuperEntityClass(DataEntity.class.getName());
        strategy.setSuperMapperClass(IBaseMapper.class.getName());
        strategy.setSuperServiceClass(IBaseService.class.getName());
        strategy.setSuperServiceImplClass(BaseServiceImpl.class.getName());
        strategy.setSuperControllerClass(CrudController.class.getName());
        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(para.getBasePackageName());
        pc.setModuleName(para.getModuleName());

        mpg.setPackageInfo(pc);
        //配置自定义属性注入
        InjectionConfig injectionConfig = new InjectionConfig() {
            //.vm模板中，通过${cfg.abc}获取属性
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
//                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                map.put("base", para.getBasePackageName());
                map.put("modules", para.getModuleName());
                map.put("fields", fields);
                map.put("underlineEntity", NamingStrategy.removePrefix(para.getTableName(), para.getTablePrefix()));

                this.setMap(map);
            }
        };

        mpg.setCfg(injectionConfig);

        // 执行生成
        mpg.execute();
        return true;
    }

    @Override
    public Boolean createFront(CreateFrontFileDTO para) {
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(Const.AUTO_CODE_PATH);
        gc.setFileOverride(true);
        gc.setActiveRecord(true);
        gc.setEnableCache(true);// XML 二级缓存
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(true);// XML columList
        gc.setAuthor("Auto Code");

        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setControllerName("%sController");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);

        dsc.setDriverName(this.driverClassName);
        dsc.setUrl(this.url);
        dsc.setUsername(this.userName);
        dsc.setPassword(UtilDatabase.getPassword(this.userName));
        mpg.setDataSource(dsc);


        //配置自定义属性注入
        InjectionConfig injectionConfig = new InjectionConfig() {
            //.vm模板中，通过${cfg.abc}获取属性
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
//                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                map.put("formItems", para.getBaseColumns());
                map.put("modules", para.getModuleName());
                map.put("tableName", para.getTableName());
                map.put("viewKey", para.getViewKey());
                this.setMap(map);
            }
        };
        String detailModelFile = "/templates/view.vue.vm";
        if (para.getDetailPageStyle().equalsIgnoreCase("son")) {
            detailModelFile = "/templates/view_son.vue.vm";
        }
        List<FileOutConfig> files = new ArrayList<FileOutConfig>();
        files.add(new FileOutConfig(detailModelFile) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return new StringBuilder().append(Const.AUTO_CODE_PATH)
                        .append("/views/")
                        .append(para.getModuleName())
                        .append("/")
                        .append(tableInfo.getEntityName().toLowerCase())
                        .append("/detail.vue").toString();
            }
        });
        String listModelFile = "/templates/index.vue.vm";
        if (para.getListPageStyle().equalsIgnoreCase("tree")) {
            listModelFile = "/templates/index_tree.vue.vm";
        } else if (para.getListPageStyle().equalsIgnoreCase("edit")) {
            listModelFile = "/templates/index_edit.vue.vm";
        }
        files.add(new FileOutConfig(listModelFile) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return new StringBuilder().append(Const.AUTO_CODE_PATH)
                        .append("/views/")
                        .append(para.getModuleName())
                        .append("/")
                        .append(tableInfo.getEntityName().toLowerCase())
                        .append("/index.vue").toString();
            }
        });
        injectionConfig.setFileOutConfigList(files);

        mpg.setCfg(injectionConfig);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 使用自定义模板，不想要生成就设置为null,如果不设置null会使用默认模板
        templateConfig.setEntity(null);
        templateConfig.setMapper(null);
        templateConfig.setXml(null);
        templateConfig.setService(null);
        templateConfig.setServiceImpl(null);
        templateConfig.setController(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setEntityLombokModel(true);
        // strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意
        strategy.setTablePrefix(new String[]{para.getTablePrefix()});// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        strategy.setInclude(new String[]{para.getTableName()}); // 需要生成的表
        // strategy.setExclude(new String[]{"test"}); // 排除生成的表
        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.scs");
        pc.setModuleName(para.getModuleName());
        mpg.setPackageInfo(pc);

        // 执行生成
        mpg.execute();
        return true;
    }

    @Override
    public Boolean createAll(CreateCRUDFileDTO crudFileDTO) {
        String entityColumn = "",formItem ="";

        List<TableColumnDTO> columnList = jdbcTemplateDefault.query(
                "select concat(table_name,'_',column_name) as  tablecolumn, table_name as tablename,column_name as columnname,data_type as datatype,character_maximum_length as maxlen,column_comment as columncomment " +
                        "from INFORMATION_SCHEMA.Columns where  table_schema=(select database()) and table_name ='"+crudFileDTO.getTableName().toLowerCase()+"'", new BeanPropertyRowMapper<TableColumnDTO>(TableColumnDTO.class));;

        if (columnList != null && columnList.size() > 0) {
            String remark = "",columnnamel="",columnnameup="",columnType="",formItemType="";
            boolean isBool= false,isNum= false,isDate = false;
            List<String> entityColNot = Arrays.asList("id","creator","gmtCreate","modifier","gmtModified","flowStatus","flowModified","flowModifier","flowModifierCode","printTime","flagPrint","snum","flowCloseRemark");
            for (TableColumnDTO column : columnList) {
                remark = column.getColumncomment();
                if (StringUtils.isBlank(remark)) {
                    remark = column.getColumnname();
                }
                columnnamel = column.getColumnname().toLowerCase();
                columnnameup = StringUtils.underline2Camel(column.getColumnname(), true);
                if (StringUtils.isNotBlank(remark) && remark.indexOf("|") !=0) {
                    remark = remark.split("\\|")[0];
                }
                isBool= false;
                isNum= false;
                isDate = false;
                if (columnnamel.indexOf("flag") != -1) {
                    columnType = "Boolean";
                    formItemType = " :type=\"constCommon.formItemType.switch.name\" ";
                } else if (columnnamel.indexOf("sku") != -1 || columnnamel.indexOf("amount") != -1 || columnnamel.indexOf("aount") != -1 ){
                    isNum = true;
                    columnType = "Double";
                    formItemType = " :type=\"constCommon.formItemType.inputNumber.name\" ";

                } else if (columnnamel.indexOf("date") != -1 || columnnamel.indexOf("time") != -1 ){
                    isDate = true;
                    columnType = "Date";
                    formItemType = " :type=\"constCommon.formItemType.datePicker.name\" ";

                } else{
                    columnType = "String";
                    formItemType = " ";
                }

                if (!entityColNot.contains(columnnameup)) {
                    entityColumn+= StrUtil.format("    //{}",remark);
                    entityColumn+= GlobalConsts.SpecialSymbol.WRAP;
                    entityColumn+= StrUtil.format("    private {} {};",columnType, columnnameup);
                    entityColumn+= GlobalConsts.SpecialSymbol.WRAP;
                    entityColumn+= GlobalConsts.SpecialSymbol.WRAP;
                }

                if (!entityColNot.contains(columnnameup)) {
                    formItem+=StrUtil.format("              <customFormItem label=\"{}\" name=\"{}\" :parent=\"_self\" :form=\"form\" {}/>",remark, columnnameup,formItemType);
                    formItem+= GlobalConsts.SpecialSymbol.WRAP;
                    formItem+= GlobalConsts.SpecialSymbol.WRAP;
                }
            }
        }
        // 后端
        if (StringUtils.isNotBlank(crudFileDTO.getBuildPathServer())) {
            File dirModelServer = null;
            try {
                dirModelServer = ResourceUtils.getFile("classpath:templates/model_server");
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
            File dirServerTo  = new File(crudFileDTO.getBuildPathServer());
            creatFile(crudFileDTO,dirModelServer,dirServerTo,entityColumn,formItem);
        }


        // 前端
        if (StringUtils.isNotBlank(crudFileDTO.getBuildPathWeb())) {
            File dirModelWeb = null;
            try {
                dirModelWeb = ResourceUtils.getFile("classpath:templates/model_web");
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
            File dirWebTo  = new File(crudFileDTO.getBuildPathWeb() +"/" + crudFileDTO.getTableName().toLowerCase());
            creatFile(crudFileDTO,dirModelWeb,dirWebTo,entityColumn,formItem);
        }

        return false;
    }

    private void creatFile(CreateCRUDFileDTO param,File dirFrom,File dirTo,String entityColumn,String formItem) {
        Collection<File> fileList =  FileUtils.listFiles(dirFrom, FileFilterUtils.fileFileFilter(), DirectoryFileFilter.INSTANCE);
        BusinessException.throwErrorIf(fileList == null || fileList.size() == 0, "未找到模板文件");

        String tableName = param.getTableName();
        // 表名 大写
        String tableu = StringUtils.underline2Camel(tableName, false);

        //表名 小写
        String tablel = tableName.toLowerCase();
        // 模板文件后缀
        String nameEnd=".model";
        //类访问路径
        String pathClass="/" + tableName.toLowerCase().replaceAll("_","/");

        try {
            // 创建目标路径
            FileUtils.forceMkdirParent(dirTo);

            // 复制所有文件
            FileUtils.copyDirectory(dirFrom,dirTo);

            List<File> fileNewList =  FileUtil.loopFiles(dirTo);
            String nameNew;
            List<String> fileLineList,fileLineListNew;
            for(File fileNew : fileNewList) {
                // 替换文件内容
                fileLineList = FileUtil.readLines(fileNew, CharsetUtil.UTF_8);
                fileLineListNew = new ArrayList<>();
                for(String line: fileLineList) {
                    line = line.replaceAll("#tableu#",tableu);
                    line = line.replaceAll("#tablel#",tablel);
                    line = line.replaceAll("#pathClass#",pathClass);
                    line = line.replaceAll("#viewName#",param.getViewName());
                    line = line.replaceAll("#viewKey#",param.getViewKey());
                    line = line.replaceAll("#basePackageName#",param.getBasePackageName());
                    line = line.replaceAll("#entityColumn#",entityColumn);
                    line = line.replaceAll("#formItem#",formItem);

                    fileLineListNew.add(line);
                }
                FileUtil.writeLines(fileLineListNew,fileNew, CharsetUtil.UTF_8);

                // 替换文件名
                nameNew = fileNew.getName().replaceAll(nameEnd,"");
                nameNew = nameNew.replaceAll("tableu",tableu);
                nameNew = nameNew.replaceAll("tablel",tablel);
                FileUtil.rename(fileNew,nameNew, true);
            }
        } catch (IOException e) {
            log.error("文件拷贝失败",e);
            throw new BusinessException("文件拷贝失败");
        }
    }
}
